from datetime import datetime
from typing import Callable
from uuid import uuid4
from fastapi import Request, Response, HTTPException, status
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError
from starlette.exceptions import HTTPException as StarletteHTTPException
from starlette.middleware.base import BaseHTTPMiddleware
import logging
import time
import traceback

logger = logging.getLogger(__name__)


class ErrorHandlingMiddleware(BaseHTTPMiddleware):
    """Middleware for handling errors and formatting responses."""
    
    async def dispatch(self, request: Request, call_next: Callable) -> Response:
        request_id = str(uuid4())
        request.state.request_id = request_id
        
        start_time = time.time()
        
        try:
            response = await call_next(request)
            
            # Add request ID to response headers
            response.headers["X-Request-ID"] = request_id
            
            # Log successful requests
            process_time = time.time() - start_time
            logger.info(
                f"Request completed: {request.method} {request.url.path} "
                f"Status: {response.status_code} Time: {process_time:.3f}s "
                f"Request-ID: {request_id}"
            )
            
            return response
            
        except Exception as exc:
            process_time = time.time() - start_time
            logger.error(
                f"Request failed: {request.method} {request.url.path} "
                f"Error: {str(exc)} Time: {process_time:.3f}s "
                f"Request-ID: {request_id}\n"
                f"Traceback: {traceback.format_exc()}"
            )
            
            # Re-raise to be handled by exception handlers
            raise


async def http_exception_handler(request: Request, exc: HTTPException) -> JSONResponse:
    """Handle HTTP exceptions."""
    request_id = getattr(request.state, "request_id", str(uuid4()))
    
    if exc.status_code == status.HTTP_401_UNAUTHORIZED:
        error_code = "AUTH_001"
        error_message = exc.detail or "Authentication failed, please login again"
    elif exc.status_code == status.HTTP_403_FORBIDDEN:
        error_code = "AUTH_002"
        error_message = exc.detail or "Insufficient permissions"
    elif exc.status_code == status.HTTP_404_NOT_FOUND:
        error_code = "RESOURCE_001"
        error_message = exc.detail or "Resource not found"
    elif exc.status_code == status.HTTP_429_TOO_MANY_REQUESTS:
        error_code = "RATE_001"
        error_message = exc.detail or "Too many requests, please try again later"
    else:
        error_code = "SERVER_001"
        error_message = exc.detail or "An error occurred"
    
    return JSONResponse(
        status_code=exc.status_code,
        content={
            "success": False,
            "error": {
                "code": error_code,
                "message": error_message,
                "details": {},
                "timestamp": datetime.utcnow().isoformat(),
                "requestId": request_id
            }
        },
        headers={"X-Request-ID": request_id}
    )


async def validation_exception_handler(request: Request, exc: RequestValidationError) -> JSONResponse:
    """Handle validation errors."""
    request_id = getattr(request.state, "request_id", str(uuid4()))
    
    # Extract validation error details
    errors = []
    for error in exc.errors():
        field = ".".join(str(loc) for loc in error["loc"] if loc != "body")
        errors.append({
            "field": field,
            "message": error["msg"],
            "type": error["type"]
        })
    
    return JSONResponse(
        status_code=status.HTTP_400_BAD_REQUEST,
        content={
            "success": False,
            "error": {
                "code": "VALIDATION_001",
                "message": "Validation error",
                "details": {"errors": errors},
                "timestamp": datetime.utcnow().isoformat(),
                "requestId": request_id
            }
        },
        headers={"X-Request-ID": request_id}
    )


async def general_exception_handler(request: Request, exc: Exception) -> JSONResponse:
    """Handle general exceptions."""
    request_id = getattr(request.state, "request_id", str(uuid4()))
    
    logger.error(
        f"Unhandled exception: {str(exc)} "
        f"Request-ID: {request_id}\n"
        f"Traceback: {traceback.format_exc()}"
    )
    
    return JSONResponse(
        status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
        content={
            "success": False,
            "error": {
                "code": "SERVER_001",
                "message": "Internal server error",
                "details": {},
                "timestamp": datetime.utcnow().isoformat(),
                "requestId": request_id
            }
        },
        headers={"X-Request-ID": request_id}
    )


class RequestLoggingMiddleware(BaseHTTPMiddleware):
    """Middleware for logging requests."""
    
    async def dispatch(self, request: Request, call_next: Callable) -> Response:
        # Log request
        logger.info(
            f"Incoming request: {request.method} {request.url.path} "
            f"Client: {request.client.host if request.client else 'Unknown'}"
        )
        
        # Process request
        response = await call_next(request)
        
        return response